/**
*
* Licensed Property to CMB Co., Ltd.
* 
* (C) Copyright of CMB Co., Ltd. 2010
*     All Rights Reserved.
*
* 
* Modification History:
* =============================================================================
*   Author         Date          Description
*   ------------ ---------- ---------------------------------------------------
*   wshe       2019-03-20       证书工具类.
* =============================================================================
*/
package com.fingard.dsp.bank.directbank.cmb04.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @ClassName: CertUtil
* @Description: acpsdk证书工具类，主要用于对证书的加载和使用
* 声明：以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己需要，按照技术文档编写。该代码仅供参考，不提供编码，性能，规范性等方面的保障<br>
*/
public class CertUtil {
	
	/** 证书容器，存储对商户请求报文签名私钥证书. */
	private static KeyStore keyStore = null;
	/** 验签公钥证书 */
	private static X509Certificate signValidCert = null;

	/** 商户私钥存储Map */
	@SuppressWarnings("unused")
	private final static Map<String, KeyStore> keyStoreMap = new ConcurrentHashMap<String, KeyStore>();
	
	static {
		init();
	}
	
	/**
	 * 初始化所有证书.
	 */
	private static void init() {
		try {
			addProvider();//向系统添加BC provider
			initSignCert();//初始化签名私钥证书
			//initValidateCertFromDir();//初始化所有的验签证书
		} catch (Exception e) {
			System.out.println("init失败。（如果是用对称密钥签名的可无视此异常。）"+e);
		}
	}
	
	/**
	 * 添加签名，验签，加密算法提供者
	 */
	private static void addProvider(){
		if (Security.getProvider("BC") == null) {
			System.out.println("add BC provider");
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} else {
			Security.removeProvider("BC"); //解决eclipse调试时tomcat自动重新加载时，BC存在不明原因异常的问题。
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			System.out.println("re-add BC provider");
		}
	}
	
	/**
	 * 用配置文件acp_sdk.properties中配置的私钥路径和密码 加载签名证书
	 */
	private static void initSignCert() {

		if (SDKConfig.getConfig().getSignCertPath() == null 
				|| SDKConfig.getConfig().getSignCertPwd() == null
				|| SDKConfig.getConfig().getSignCertType() == null) {
			System.out.println("WARN: " + SDKConfig.SDK_SIGNCERT_PATH + "或" + SDKConfig.SDK_SIGNCERT_PWD 
					+ "或" + SDKConfig.SDK_SIGNCERT_TYPE + "为空。 停止加载签名证书。");
			return;
		}
		if (null != keyStore) {
			keyStore = null;
		}
		try {
			keyStore = getKeyInfo(SDKConfig.getConfig().getSignCertPath(),
					SDKConfig.getConfig().getSignCertPwd(), SDKConfig
							.getConfig().getSignCertType());
		} catch (IOException e) {
			System.out.println("InitSignCert Error"+ e);
		}
	}

	/**
	 * 通过证书路径初始化为公钥证书
	 * @param path
	 * @return
	 */
	private static X509Certificate initCert(String path) {
		X509Certificate validateCertTemp = null;
		CertificateFactory cf = null;
		FileInputStream in = null;
		try {
			cf = CertificateFactory.getInstance("X.509", "BC");
			in = new FileInputStream(path);
			validateCertTemp = (X509Certificate) cf.generateCertificate(in);
			// 打印证书加载信息,供测试阶段调试
			System.out.println("[" + path + "][CertId="
					+ validateCertTemp.getSerialNumber().toString() + "]");
		} catch (CertificateException e) {
			System.out.println("InitCert Error"+ e);
		} catch (FileNotFoundException e) {
			System.out.println("InitCert Error File Not Found"+ e);
		} catch (NoSuchProviderException e) {
			System.out.println("LoadVerifyCert Error No BC Provider"+ e);
		} finally {
			if (null != in) {
				try {
					in.close();
				} catch (IOException e) {
					System.out.println(e.toString());
				}
			}
		}
		return validateCertTemp;
	}
	
	/**
	 * 通过keyStore 获取私钥签名证书PrivateKey对象
	 * 
	 * @return
	 */
	public static PrivateKey getSignCertPrivateKey() {
		try {
			Enumeration<String> aliasenum = keyStore.aliases();
			String keyAlias = null;
			if (aliasenum.hasMoreElements()) {
				keyAlias = aliasenum.nextElement();
			}
			PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias,
					SDKConfig.getConfig().getSignCertPwd().toCharArray());
			return privateKey;
		} catch (KeyStoreException e) {
			System.out.println("getSignCertPrivateKey Error"+ e);
			return null;
		} catch (UnrecoverableKeyException e) {
			System.out.println("getSignCertPrivateKey Error"+ e);
			return null;
		} catch (NoSuchAlgorithmException e) {
			System.out.println("getSignCertPrivateKey Error"+ e);
			return null;
		}
	}
	
	/**
	 * 获取公钥验签证书PublicKey
	 * 
	 * @return
	 */
	public static PublicKey getValidateCertPublicKey() {
		if (null == signValidCert) {
			String path = SDKConfig.getConfig().getValidateCertPath();
			if (!SDKUtil.isEmpty(path)) {
				signValidCert = initCert(path);
				return signValidCert.getPublicKey();
			} else {
				System.out.println("acpsdk.validateCert.path is empty");
				return null;
			}
		} else {
			return signValidCert.getPublicKey();
		}
	}


	/**
	 * 将签名私钥证书文件读取为证书存储对象
	 * 
	 * @param pfxkeyfile
	 *            证书文件名
	 * @param keypwd
	 *            证书密码
	 * @param type
	 *            证书类型
	 * @return 证书对象
	 * @throws IOException 
	 */
	private static KeyStore getKeyInfo(String pfxkeyfile, String keypwd,
			String type) throws IOException {
		System.out.println("加载签名证书==>" + pfxkeyfile);
		FileInputStream fis = null;
		try {
			KeyStore ks = KeyStore.getInstance(type, "BC");
			System.out.println("Load RSA CertPath=[" + pfxkeyfile + "],Pwd=["+ keypwd + "],type=["+type+"]");
			fis = new FileInputStream(pfxkeyfile);
			char[] nPassword = null;
			nPassword = null == keypwd || "".equals(keypwd.trim()) ? null: keypwd.toCharArray();
			if (null != ks) {
				ks.load(fis, nPassword);
			}
			return ks;
		} catch (Exception e) {
			System.out.println("getKeyInfo Error"+ e);
			return null;
		} finally {
			if(null!=fis)
				fis.close();
		}
	}
			
	
	/**
	 * 证书文件过滤器
	 * 
	 */
	static class CerFilter implements FilenameFilter {
		public boolean isCer(String name) {
			if (name.toLowerCase().endsWith(".cer")) {
				return true;
			} else {
				return false;
			}
		}
		public boolean accept(File dir, String name) {
			return isCer(name);
		}
	}

}

